package com.mc.fastkit.utils

import android.media.MediaScannerConnection
import android.webkit.MimeTypeMap
import androidx.annotation.IntRange
import com.mc.fastkit.ext.app
import java.io.File
import java.io.FileFilter

/**
 * 文件操作工具类
 * @author: MasterChan
 * @date: 2023-02-21 13:45:04
 */
object FileUtils {

    /**
     * 判断文件是否存在
     * @param file File?
     * @return Boolean
     */
    fun isExist(file: File?): Boolean {
        return file?.exists() ?: return false
    }

    /**
     * 判断是否是文件夹
     * @param path String?
     * @return Boolean
     */
    fun isDir(path: String?): Boolean {
        return isDir(generateFile(path))
    }

    /**
     * 判断是否是文件夹
     * @param file File?
     * @return Boolean
     */
    fun isDir(file: File?): Boolean {
        return isExist(file) && file!!.isDirectory
    }

    /**
     * 判断是否是文件
     * @param path String?
     * @return Boolean
     */
    fun isFile(path: String?): Boolean {
        return isFile(generateFile(path))
    }

    /**
     * 判断是否是文件
     * @param file File?
     * @return Boolean
     */
    fun isFile(file: File?): Boolean {
        return isExist(file) && file!!.isFile
    }

    /**
     * 创建文件夹；如果不存在则创建，存在不做任何
     * @param path String?
     * @return Boolean
     */
    fun createDir(path: String?): Boolean {
        return createDir(generateFile(path))
    }

    /**
     * 创建文件夹；如果不存在则创建，存在不做任何
     * @param file File?
     * @return Boolean
     */
    fun createDir(file: File?): Boolean {
        file ?: return false
        return if (isExist(file)) file.isDirectory else file.mkdirs()
    }

    /**
     * 创建文件；如果不存在则创建，存在不做任何
     * @param path String?
     * @return Boolean
     */
    fun createFile(path: String?): Boolean {
        return createFile(generateFile(path))
    }

    /**
     * 创建文件；如果不存在则创建，存在不做任何
     * @param file File?
     * @return Boolean
     */
    fun createFile(file: File?): Boolean {
        file ?: return false
        if (file.exists()) return file.isFile
        if (!createDir(file.parentFile)) return false
        return try {
            file.createNewFile()
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    /**
     * 创建文件；如果文件不存在则创建，存在则删除老文件后创建
     * @param path String?
     * @return Boolean
     */
    fun createFileAndDelOldFile(path: String?): Boolean {
        return createFileAndDelOldFile(generateFile(path))
    }

    /**
     * 创建文件；如果文件不存在则创建，存在则删除老文件后创建
     * @param file File?
     * @return Boolean
     */
    fun createFileAndDelOldFile(file: File?): Boolean {
        file ?: return false
        if (file.exists() && !file.delete()) return false
        if (!createDir(file.parentFile)) return false
        return try {
            file.createNewFile()
        } catch (e: Exception) {
            e.printStackTrace()
            false
        }
    }

    /**
     * 删除文件夹及子文件夹中的所有文件
     * @param path String?
     * @param keepSelf 是否保留自己不被删除；true：不删除，false：删除
     * @return 如果文件不存在返回true，只要有一个文件删除失败则返回false
     */
    fun deleteDir(path: String?, keepSelf: Boolean = false): Boolean {
        return deleteDir(generateFile(path), keepSelf)
    }

    /**
     * 删除文件夹及子文件夹中的所有文件
     * @param dir File?
     * @param keepSelf 是否保留自己不被删除；true：不删除，false：删除
     * @return 如果文件夹不存在返回true，只要有一个文件删除失败则返回false
     */
    fun deleteDir(dir: File?, keepSelf: Boolean = false): Boolean {
        dir ?: return false
        if (!dir.exists()) return true
        if (!dir.isDirectory) return false
        dir.listFiles()?.forEach {
            if (it.isFile) {
                if (!it.delete()) return false
            } else if (it.isDirectory) {
                if (!deleteDir(it)) return false
            }
        }
        return !keepSelf || dir.delete()
    }

    /**
     * 删除文件
     * @param path String?
     * @return 如果文件不存在返回true，删除失败返回false
     */
    fun deleteFile(path: String?): Boolean {
        return deleteFile(generateFile(path))
    }

    /**
     * 删除文件
     * @param file File?
     * @return 如果文件不存在返回true，删除失败返回false
     */
    fun deleteFile(file: File?): Boolean {
        file ?: return false
        return !file.exists() || file.isFile && file.delete()
    }

    /**
     * 删除文件或者文件夹及其子文件夹内的所有文件
     * @param file File?
     * @return 如果文件不存在返回true，删除失败返回false
     */
    fun delete(file: File?): Boolean {
        file ?: return false
        return if (file.isDirectory) {
            deleteDir(file)
        } else {
            deleteFile(file)
        }
    }

    /**
     * 删除目录中满足过滤器的所有文件
     * @param path String?
     * @param filter FileFilter
     * @return Boolean
     */
    fun deleteWithFilter(path: String?, filter: FileFilter): Boolean {
        return deleteWithFilter(generateFile(path), filter)
    }

    /**
     * 删除目录中满足过滤器的所有文件
     * @param dir File?
     * @param filter FileFilter
     * @return Boolean
     */
    fun deleteWithFilter(dir: File?, filter: FileFilter): Boolean {
        dir ?: return false
        if (dir.exists()) return true
        if (!dir.isDirectory) return false
        dir.listFiles()?.forEach {
            if (filter.accept(it)) {
                if (it.isFile) {
                    if (!it.delete()) return false
                } else if (it.isDirectory) {
                    if (!deleteDir(it)) return false
                }
            }
        }
        return true
    }

    /**
     * 获取文件夹以及子文件夹中所有符合过滤器的文件
     * @param dir File?
     * @param filter 如果过滤器为空，则默认全部文件
     * @return MutableList<File>
     */
    fun listFiles(dir: File?, filter: FileFilter? = null): MutableList<File> {
        val list = mutableListOf<File>()
        if (!isDir(dir)) return list
        dir?.listFiles()?.forEach {
            if (it.isDirectory) {
                list.addAll(listFiles(it, filter))
            } else if (it.isFile) {
                if (filter == null || filter.accept(it)) {
                    list.add(it)
                }
            }
        }
        return list
    }

    /**
     * 重命名文件
     * @param srcPath 源路径
     * @param destName 目标文件名
     * @return Boolean
     */
    fun rename(srcPath: String?, destName: String?): Boolean {
        return rename(generateFile(srcPath), destName)
    }

    /**
     * 重命名文件
     * @param srcFile File?
     * @param destName 目标文件名
     * @return Boolean
     */
    fun rename(srcFile: File?, destName: String?): Boolean {
        if (isExist(srcFile)) return false
        if (destName.isNullOrEmpty()) return false
        if (srcFile!!.name == destName) return true
        val newFile = File("${srcFile.parent}/$destName")
        return !newFile.exists() && srcFile.renameTo(newFile)
    }

    /**
     * 复制文件或文件夹
     * @param src 源文件
     * @param dest 目标文件
     * @return Boolean
     */
    fun copy(src: File?, dest: File?): Boolean {
        src ?: return false
        if (src.isDirectory) {
            return copyDir(src, dest)
        }
        return copyFile(src, dest)
    }

    /**
     * 复制文件夹
     * @param srcDir 源文件夹
     * @param destPath 目标文件夹
     * @return Boolean
     */
    fun copyDir(srcDir: File?, destPath: String?): Boolean {
        return copyDir(srcDir, generateFile(destPath))
    }

    /**
     * 复制文件夹
     * @param srcDir 源文件夹
     * @param destDir 目标文件夹
     * @return Boolean
     */
    fun copyDir(srcDir: File?, destDir: File?): Boolean {
        return copyOrMoveDir(srcDir, destDir, false)
    }

    /**
     * 复制文件
     * @param srcFile 源文件
     * @param destPath 目标文件
     * @return Boolean
     */
    fun copyFile(srcFile: File?, destPath: String?): Boolean {
        return copyFile(srcFile, generateFile(destPath))
    }

    /**
     * 复制文件
     * @param srcFile 源文件
     * @param destFile 目标文件
     * @return Boolean
     */
    fun copyFile(srcFile: File?, destFile: File?): Boolean {
        return copyOrMoveFile(srcFile, destFile, false)
    }

    /**
     * 移动文件或文件夹
     * @param src 源文件
     * @param dest 目标文件
     * @return Boolean
     */
    fun move(src: File?, dest: File?): Boolean {
        src ?: return false
        if (src.isDirectory) {
            return moveDir(src, dest)
        }
        return moveFile(src, dest)
    }

    /**
     * 移动文件夹
     * @param srcDir 源文件夹
     * @param destPath 目标文件夹
     * @return Boolean
     */
    fun moveDir(srcDir: File?, destPath: String?): Boolean {
        return moveDir(srcDir, generateFile(destPath))
    }

    /**
     * 移动文件夹
     * @param srcDir 源文件夹
     * @param destDir 目标文件夹
     * @return Boolean
     */
    fun moveDir(srcDir: File?, destDir: File?): Boolean {
        return copyOrMoveDir(srcDir, destDir, true)
    }

    /**
     * 移动文件
     * @param srcFile 源文件
     * @param destPath 目标文件
     * @return Boolean
     */
    fun moveFile(srcFile: File?, destPath: String?): Boolean {
        return moveFile(srcFile, generateFile(destPath))
    }

    /**
     * 移动文件
     * @param srcFile 源文件
     * @param destFile 目标文件
     * @return Boolean
     */
    fun moveFile(srcFile: File?, destFile: File?): Boolean {
        return copyOrMoveFile(srcFile, destFile, true)
    }

    /**
     * 复制或移动文件夹
     * @param srcDir 源文件
     * @param destDir 目标文件
     * @param isMove 是否是移动文件，true：删除源文件
     * @return Boolean
     */
    private fun copyOrMoveDir(srcDir: File?, destDir: File?, isMove: Boolean): Boolean {
        srcDir ?: return false
        destDir ?: return false
        //不能将源文件夹移动到源文件夹的下级目录
        if (destDir.path.contains(srcDir.path)) return false
        if (!isDir(srcDir)) return false
        if (!createDir(destDir)) return false
        srcDir.listFiles()?.forEach {
            val destFile = File("${destDir.path}/${it.name}")
            if (it.isFile) {
                if (!copyOrMoveFile(it, destFile, isMove)) return false
            } else {
                if (!copyOrMoveDir(it, destDir, isMove)) return false
            }
        }
        return !isMove || deleteDir(srcDir)
    }

    /**
     * 复制或者移动文件
     * @param srcFile 源文件
     * @param destFile 目标文件
     * @param isMove 是否是移动文件，true：删除源文件
     * @return Boolean
     */
    private fun copyOrMoveFile(srcFile: File?, destFile: File?, isMove: Boolean): Boolean {
        srcFile ?: return false
        destFile ?: return false
        if (srcFile == destFile) return false
        if (!srcFile.exists() || !srcFile.isFile) return false
        if (destFile.exists()) return true
        if (!createFile(destFile)) return false
        srcFile.inputStream().copyTo(destFile.outputStream()) > 0
        return !(isMove && !deleteFile(srcFile))
    }

    /**
     * 通知系统扫描指定文件
     * @param filePath String?
     */
    fun notifySystemScan(filePath: String?) {
        notifySystemScan(generateFile(filePath))
    }

    /**
     * 通知系统扫描指定文件
     * @param file File?
     */
    fun notifySystemScan(file: File?) {
        if (file == null || !file.exists()) return
        MediaScannerConnection.scanFile(app, arrayOf(file.path), null, null)
    }

    /**
     * 获取带单位的文件大小
     * @param file File?
     * @return String
     */
    fun getFileSizeString(file: File?): String {
        return convert2StringSize(getFileSize(file))
    }

    /**
     * 如果是文件，返回文件大小；如果是文件夹，返回包括其子文件夹在内的所有文件的大小
     * @param file File?
     * @return Long
     */
    fun getFileSize(file: File?): Long {
        if (!isExist(file)) return 0
        var length = -1L
        if (file!!.isFile) {
            length = file.length()
        } else if (file.isDirectory) {
            listFiles(file).forEach { length += it.length() }
        }
        return length
    }

    /**
     * 将文件大小转为带单位的大小
     * @param length Long
     * @param precision Int
     * @return String
     */
    fun convert2StringSize(length: Long, @IntRange(from = 1, to = 10) precision: Int = 3): String {
        val kb = 1024
        val mb = 1024 * 1024
        val gb = 1024 * 1024 * 1024
        return when {
            length < 0 -> "0KB"
            length < kb -> String.format("%.${precision}fB", length.toDouble())
            length < mb -> String.format("%.${precision}fKB", length.toDouble() / kb)
            length < gb -> String.format("%.${precision}fMB", length.toDouble() / mb)
            else -> String.format("%.${precision}fGB", length.toDouble() / gb)
        }
    }

    /**
     * 根据扩展名获取mimeType
     * @param file File?
     * @return String?
     */
    fun getMimeType(file: File?): String? {
        file ?: return null
        return getMimeType(file.extension)
    }

    /**
     * 根据扩展名获取mimeType
     * @param extension String
     * @return String?
     */
    fun getMimeType(extension: String): String? {
        return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
    }

    fun generateFile(path: String?): File? {
        return if (path.isNullOrEmpty()) null else File(path)
    }
}